using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace Nairo.Parser.Dom
{
	public class HtmlTag : AbstractBlock
	{
		protected const string SPACEINSTRING = "&__SPACEINSTRING__;";
		protected const string EQUINSTRING = "&__EQUINSTRING__;";
		protected const string NOVALUE = "&__NOVALUE__;";

		protected Dictionary<string, string> attributes;
		protected bool inLine;
		protected string tagName;
		protected int marginTop = 0, marginBottom = 0;
		
		// Basic HTML attributes \\
		
		protected string @class = "";
		protected string id = "";
		protected string style = "";
		protected string title = "";

		#region Properties
		public bool InLine {
			get { return inLine; }
			set { inLine = value; }
		}
		
		public Dictionary<string, string> Attributes {
			get { return attributes; }
			set { attributes = value; }
		}
		
		public string InnerText {
			get {
				InnerHTMLBlock block = GetInnerText();
				if (block == null) {
					return "";
				} else {
					return block.Text;
				}
			}
			set {
				this.SetInnerText(value);
			}
		}
		
		public string TagName {
			get { return tagName; }
			set { tagName = value; }
		}
		
		public int MarginTop {
			get { return marginTop; }
			set { marginTop = value; }
		}
		
		public int MarginBottom {
			get { return marginBottom; }
			set { marginBottom = value; }
		}
		
		public string Class {
			get { return @class; }
			set { @class = value; }
		}
		
		public string Id {
			get { return id; }
			set { id = value; }
		}
		
		public string Style {
			get { return style; }
			set { style = value; }
		}
		
		public string Title {
			get { return title; }
			set { title = value; }
		}
		#endregion
		
		public HtmlTag(FileUnit file, string tagName, INode parent, object userData)
			: this(file, tagName, new Dictionary<string, string>(), parent, userData)
		{
		}
		
		public HtmlTag(FileUnit file, string tagName, Dictionary<string, string> attributes, INode parent, object userData)
			: base(file, parent, new List<INode>(), userData)
		{
			this.tagName = tagName;
			this.attributes = attributes;
			
			switch (tagName) {
				case "b":
				case "i":
				case "u":
				case "span":
					this.inLine = true;
					break;
				default:
					this.inLine = false;
					break;
			}
			
			if((parent != null) && (parent is HtmlTag))
			{
				HtmlTag tag = parent as HtmlTag;
				this.Parent=parent;			}
			else
			{
				//If no parent, start with default settings
//				this.Alignment=HTMLAlignment.Left;
//				this.VAlignment=HTMLVAlignment.Bottom;
				this.marginTop = 19;
				this.marginBottom = 19;
			}
		}
		
		private InnerHTMLBlock GetInnerText()
		{
			foreach (INode node in this.Children) {
				if (node is InnerHTMLBlock)
					return node as InnerHTMLBlock;
			}
			
			return null;
		}
		
		private void SetInnerText(string text)
		{
			InnerHTMLBlock inner = null;
			foreach (INode node in this.Children) {
				if (node is InnerHTMLBlock) {
					inner = node as InnerHTMLBlock;
					break;
				}
			}
			
			if (inner != null) {
				inner.Text = text;
			} else {
				inner = new InnerHTMLBlock(this.File, text, this, null);
				this.Children.Add(inner);
			}
		}
		
		private void SetInnerText(InnerHTMLBlock block)
		{
			InnerHTMLBlock inner = null;
			foreach (INode node in this.Children) {
				if (node is InnerHTMLBlock) {
					inner = node as InnerHTMLBlock;
					break;
				}
			}
			
			if (inner != null) {
				inner.Text = block.Text;
			} else {
				inner = new InnerHTMLBlock(this.File, block.Text, this, null);
				this.Children.Add(inner);
			}
		}
		
		public static HtmlTag FromHTML(string text, INode parent)
		{
			text = ReplaceInStringTags(text);
			text = text.Replace("\"", "\" ");
			text = RemoveSpaceInString(text);
			
			while (text.Contains("  ") || text.Contains("= ") || text.Contains(" =")) {
				text = text.Replace("  ", " ");
				text = text.Replace("= ", "=");
				text = text.Replace(" =", "=");
			}
			
			text = text.Trim('>', '<', ' ', '/');
			
			string[] items = text.Split(' ');
			
			string tagName = items[0];
			
			Dictionary<string, string> attributes = new Dictionary<string, string>();
			
			for (int i = 1; i < items.Length; i++) {
				items[i] = items[i].Replace("\"", "");
				string[] _pair = items[i].Split('=');
				attributes.Add(_pair[0], ((_pair.Length == 1) ? NOVALUE : _pair[1]));
			}
			
			switch (tagName.ToLower()) {
				case "body":
					return new HtmlBody(parent.File, parent);
				case "p":
					return new HtmlParagraph(parent.File, parent);
				case "h1":
					return new HtmlHeader1(parent.File, parent);
				case "h2":
					return new HtmlHeader2(parent.File, parent);
				case "h3":
					return new HtmlHeader3(parent.File, parent);
				case "h4":
					return new HtmlHeader4(parent.File, parent);
				case "h5":
					return new HtmlHeader5(parent.File, parent);
				case "h6":
					return new HtmlHeader6(parent.File, parent);
			}
			
			return new HtmlTag(parent.File, tagName, attributes, parent, null);
		}
		
		private static string ReplaceInStringTags(string text)
		{
			StringBuilder buffer = new StringBuilder();
			bool inString = false;
			
			for (int i = 0; i < text.Length; i++) {
				if (text[i] == '"')
					inString = !inString;
				
				if (text[i] == ' ' && inString)
					buffer.Append(SPACEINSTRING);
				else {
					if (text[i] == '=' && inString)
						buffer.Append(EQUINSTRING);
					else
						buffer.Append(text[i]);
				}
			}
			
			return buffer.ToString();
		}
		
		private static string RemoveSpaceInString(string text)
		{
			StringBuilder buffer = new StringBuilder();
			bool inString = false;
			
			for (int i = 0; i < text.Length; i++) {
				if (text[i] == '"')
					inString = !inString;
				
				if (!(text[i] == ' ' && inString))
					buffer.Append(text[i]);
			}
			
			return buffer.ToString();
		}
		
		public override INode GetTag(string name)
		{
			foreach (INode node in this.Children) {
				if (node is HtmlTag) {
					HtmlTag tmp = node as HtmlTag;
					if (tmp.TagName == name)
						return node;
				}
			}
			
			return null;
		}
		
		public override INode[] GetTags(string name)
		{
			List<INode> list = new List<INode>();
			
			foreach (INode node in Children) {
				if (node is HtmlTag) {
					HtmlTag tmp = node as HtmlTag;
					if (tmp.TagName == name)
						list.Add(node);
				}
			}
			
			return list.ToArray();
		}
		
		public string Create(bool useLowerCaseTags)
		{
			string text = "<" + ((useLowerCaseTags) ? this.tagName.ToLowerInvariant() : this.tagName);
			
			foreach (KeyValuePair<string, string> pair in this.attributes)
			{
				string val = pair.Value.Replace(SPACEINSTRING, " ");
				val = val.Replace(EQUINSTRING, "=");
				text += " " + pair.Key;
				if (val != NOVALUE)
					text += "=\"" + val + "\"";
			}
			
			if (this.Children.Count > 0) {
				text += ">";
			} else {
				text += " >";
			}
			
			return text;
		}
		
		public string CreateEnd(bool useLowerCaseTags)
		{
			return "<" + ((useLowerCaseTags) ? this.tagName.ToLowerInvariant() : this.tagName) + ">";
		}
		
		public bool HasAttribute(string name)
		{
			foreach (KeyValuePair<string, string> pair in attributes) {
				if (name == pair.Key)
					return true;
			}
			
			return false;
		}
		
		public string GetAttributeValue(string name)
		{
			foreach (KeyValuePair<string, string> pair in attributes) {
				if (name == pair.Key)
					return pair.Value;
			}
			
			return string.Empty;
		}
		
		public override object AcceptChildren(IDomVisitor visitor, object data)
		{
			throw new NotImplementedException();
		}
		
		public override object AcceptVisitor(IDomVisitor visitor, object data)
		{
			return visitor.VisitHTMLTag(this, data);
		}
		
		public override string ToString()
		{
			string attribs = "";
			
			foreach (KeyValuePair<string, string> pair in attributes)
				attribs += pair.Key + " = " + pair.Value + ", ";
			
			return string.Format(" [ TagName: {0}, Attributes: {1} ] ", this.tagName, attribs);
		}

		public List<Nairo.Parser.Dom.HtmlAttribute<object>> GetParsedAttributes()
		{
			List<HtmlAttribute<object>> list = new List<HtmlAttribute<object>>();
			
			foreach (KeyValuePair<string, string> pair in attributes) {
				Type type;
				if (TypeMap.Map.TryGetValue(pair.Key, out type)) {
					if (type == typeof( Color)) {
						list.Add(new HtmlAttribute<object>(pair.Key, TypeConverter.ConvertColor(pair.Value, Color.White)));
					}
				}
			}
			
			return list;
		}
		
		protected string ParseString(HtmlTag xnode, string aname, string sdefault)
		{
			try
			{
				string attr = xnode.GetAttributeValue(aname);
				if (attr != string.Empty)
					return attr;
				return sdefault;
			}
			catch (Exception)
			{
				return sdefault;
			}

		}

		protected int ParseInt(HtmlTag xnode, string aname, int ndefault)
		{
			string attr = xnode.GetAttributeValue(aname);
			if (attr != string.Empty) return int.Parse(attr);
			return ndefault;
		}

		protected float ParseFloat(HtmlTag xnode, string aname, float ndefault)
		{
			string attr = xnode.GetAttributeValue(aname);
			if (attr != string.Empty) return float.Parse(attr);
			return ndefault;
		}
		
		public void Layout(HtmlRenderer renderer, Graphics g)
		{
			// TODO : Remove temporary method
		}
	}
}